/* ***** BEGIN LICENSE BLOCK *****
 *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Youtube Tooltip.
 *
 * The Initial Developer of the Original Code is
 * Brandon Peters.
 * Portions created by the Initial Developer are Copyright (C) 2008
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 * ***** END LICENSE BLOCK ***** */

var youtubetooltip = {
	prefsService: null,
	prefs: {},
	originalTooltip: null,

	onLoad: function()
	{
		this.prefsService = Components.classes['@mozilla.org/preferences-service;1']
			.getService(Components.interfaces.nsIPrefService)
			.getBranch('extensions.{D48A12C2-E0F4-4640-8ADE-2DE4959E0F3D}.')
		this.prefsService.QueryInterface(Components.interfaces.nsIPrefBranch2);
		this.prefsService.addObserver('', this, false)

		var preferences = [
			'showauthor',
			'showrating',
			'showviews',
			'showduration',
			'showdate',
			'showdescription',
			'showcategory',
			'showtags',
			'showcomments',
			'showresponses'
		];

		for(var idx in preferences)
		{
			var preference = preferences[idx];
			this.prefs[preference] = this.prefsService.getBoolPref(preference);
		}
	
		this.originalTooltip = window.FillInHTMLTooltip;
		window.FillInHTMLTooltip = this.newTooltip;
	},
	onUnload: function()
	{
		this.prefsService.removeObserver('', this);
	},
	observe: function(subject, topic, data)
	{
		if(topic != 'nsPref:changed')
		{
			return;
		}

		this.prefs[data] = this.prefsService.getBoolPref(data);
	},
	_addCommas: function(num)
	{
    		var re = /(\d+)(\d{3})/;
		while(re.test(num))
		{
			num = num.replace(re, '$1' + ',' + '$2');
		}
		return num;
	},
	youtubeData: function(videoID)
	{
		var yData = {
			title: 'Video No Longer Available',
			author: 'Unknown',
			rating: 'Unknown',
			views: 'Unknown',
			duration: 'Unknown',
			date: 'Unknown',
			description: 'Unknown',
			category: 'Unknown',
			tags: 'Unknown',
			comments: 'Unknown',
			responses: 'Unknown'
		};

		/*
		 * Consider moving to REST format
		 * http://www.youtube.com/api2_rest?method=youtube.videos.get_details&dev_id=<dev_id>&video_id=<video_id>
		 */	

		const xmlNS = 'http://www.w3.org/2005/Atom';
		const ytNS = 'http://gdata.youtube.com/schemas/2007'; 
		const gdNS = 'http://schemas.google.com/g/2005';
		const mediaNS = 'http://search.yahoo.com/mrss/';
		const osNS = 'http://a9.com/-/spec/opensearchrss/1.0/';

		var gdataURL = 'http://gdata.youtube.com/feeds/api/videos/' + videoID;
		var commentURL = 'http://gdata.youtube.com/feeds/api/videos/' + videoID + '/comments';
		var responseURL = 'http://gdata.youtube.com/feeds/api/videos/' + videoID + '/responses';

		var ioserv = Components.classes['@mozilla.org/network/io-service;1'] 
			.getService(Components.interfaces.nsIIOService); 
		var bstream = Components.classes['@mozilla.org/binaryinputstream;1'] 
			.createInstance(Components.interfaces.nsIBinaryInputStream); 
		var parser = new DOMParser();

		/* Video Entry */
		var videoChannel = ioserv.newChannel(gdataURL, 0, null); 
		var videoStream = videoChannel.open(); 

		if(videoChannel instanceof Components.interfaces.nsIHttpChannel && videoChannel.responseStatus != 200)
		{ 
			return yData;
		}

		bstream.setInputStream(videoStream); 

		var videoSize = bstream.available();
		var videoDom = parser.parseFromStream(bstream, 'UTF-8', videoSize, 'application/xml');

		/* Title */
		var titles = videoDom.getElementsByTagNameNS(xmlNS, 'title');
		if(titles)
		{
			var titleElement = titles[0];
			if(titleElement && titleElement.textContent)
			{
				yData.title = titleElement.textContent;
			}
		}

		/* Author */
		var authors = videoDom.getElementsByTagNameNS(xmlNS, 'author');
		if(authors)
		{
			var authorElement = authors[0];
			if(authorElement && authorElement.firstChild)
			{
				var nameElement = authorElement.firstChild;
				if(nameElement && nameElement.textContent)
				{
					yData.author = nameElement.textContent;
				}
			}
		}

		/* Rating */
		var ratings = videoDom.getElementsByTagNameNS(gdNS, 'rating');
		if(ratings)
		{
			var ratingElement = ratings[0];
			if(ratingElement && ratingElement.getAttribute('average'))
			{
				yData.rating = Math.round(ratingElement.getAttribute('average') * 100) / 100;
			}
		}

		/* Number of Views */
		var views = videoDom.getElementsByTagNameNS(ytNS, 'statistics');
		if(views)
		{
			var viewElement = views[0];
			if(viewElement && viewElement.getAttribute('viewCount'))
			{
				yData.views = this._addCommas(viewElement.getAttribute('viewCount'));
			}
		}

		/* Duration */
		var durations = videoDom.getElementsByTagNameNS(ytNS, 'duration');
		if(durations)
		{
			var durationElement = durations[0];
			if(durationElement && durationElement.getAttribute('seconds'))
			{
				yData.duration = '';
	
				var seconds = durationElement.getAttribute('seconds');
				var minutes = 0;
				var hours = 0;
	
				if(seconds > 60)
				{
					minutes = (seconds - (seconds % 60)) / 60;
					seconds = seconds % 60;
				}
	
				if(minutes > 60)
				{
					hours = (minutes - (minutes % 60)) / 60;
					minutes = minutes % 60;
				}
	
				if(hours != 0)
				{
					yData.duration += hours + 'h';
				}
	
				if(minutes != 0)
				{
					yData.duration += minutes + 'm';
				}
	
				if(seconds != 0)
				{
					yData.duration += seconds + 's';
				}
			}
		}

		/* Date Published */
		var dates = videoDom.getElementsByTagNameNS(xmlNS, 'published');
		if(dates)
		{
			var dateElement = dates[0];
			if(dateElement && dateElement.textContent)
			{
				var re = '([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})';
				var dateStr = dateElement.textContent;
				var matches = dateStr.match(new RegExp(re));
				if(matches)
				{
					yData.date = matches[2] + '/' + matches[3] + '/' + matches[1] + ' ' + matches[4] + ':' + matches[5] + ':' + matches[6];
				}
			}
		}

		/* Description */
		var descriptions = videoDom.getElementsByTagNameNS(xmlNS, 'content');
		if(descriptions)
		{
			var descriptionElement = descriptions[0];
			if(descriptionElement && descriptionElement.textContent)
			{
				yData.description = descriptionElement.textContent;
			}
		}
		
		/* Category */
		var categories = videoDom.getElementsByTagNameNS(mediaNS, 'category');
		if(categories)
		{
			var categoryElement = categories[0];
			if(categoryElement && categoryElement.textContent)
			{
				yData.category = categoryElement.textContent;
			}
		}

		/* Tags */
		var tags = videoDom.getElementsByTagNameNS(mediaNS, 'keywords');
		if(tags)
		{
			var tagElement = tags[0];
			if(tagElement && tagElement.textContent)
			{
				yData.tags = tagElement.textContent;
			}
		}

		/* Comments Entry */
		var commentChannel = ioserv.newChannel(commentURL, 0, null); 
		var commentStream = commentChannel.open(); 

		if(commentChannel instanceof Components.interfaces.nsIHttpChannel && commentChannel.responseStatus != 200)
		{ 
			return yData;
		}

		bstream.setInputStream(commentStream); 

		var commentSize = bstream.available();
		var commentDom = parser.parseFromStream(bstream, 'UTF-8', commentSize, 'application/xml');

		/* Number of Comments */
		var comments = commentDom.getElementsByTagNameNS(osNS, 'totalResults');
		if(comments)
		{
			var commentElement = comments[0];
			if(commentElement && commentElement.textContent)
			{
				yData.comments = commentElement.textContent;
			}
		}

		/* Responses Entry */
		var responseChannel = ioserv.newChannel(responseURL, 0, null); 
		var responseStream = responseChannel.open(); 

		if(responseChannel instanceof Components.interfaces.nsIHttpChannel && responseChannel.responseStatus != 200)
		{ 
			return yData;
		}

		bstream.setInputStream(responseStream); 

		var responseSize = bstream.available();
		var responseDom = parser.parseFromStream(bstream, 'UTF-8', responseSize, 'application/xml');

		/* Number of Responses */
		var responses = responseDom.getElementsByTagNameNS(osNS, 'totalResults');
		if(responses)
		{
			var responseElement = responses[0];
			if(responseElement && responseElement.textContent)
			{
				yData.responses = responseElement.textContent;
			}
		}

		return yData;
	},
	newTooltip: function(tipElement)
	{
		var retVal = false;

		if(tipElement.namespaceURI == 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul')
		{
			return retVal;
		}

		const XLinkNS = 'http://www.w3.org/1999/xlink';

		var linkHREF = null;
		var XLinkHREF = null;
		var direction = tipElement.ownerDocument.dir;

		linkHREF = tipElement.getAttribute('href');
		XLinkHREF = tipElement.getAttributeNS(XLinkNS, 'href');

		var defView = tipElement.ownerDocument.defaultView;
		if(!defView)
		{
			return retVal;
		}
		direction = defView.getComputedStyle(tipElement, '').getPropertyValue('direction');

		var hrefs = [linkHREF, XLinkHREF];
		var tipNode = document.getElementById('aHTMLTooltip');
		tipNode.style.direction = direction;

		var patterns = [
			[/^http:\/\/([A-Za-z0-9]*)(\.?)youtube\.com\/watch\?v=([A-Za-z0-9-_]+)/, 3],
			[/^\/watch\?v=([A-Za-z0-9-_]+)/, 1]
		];

		for(var i = 0; i < hrefs.length; ++i)
		{
			var h = hrefs[i];
			for(var j = 0; j < patterns.length; ++j)
			{
				var re = patterns[j][0];
				var idx = patterns[j][1];
				if(re.test(h))
				{
					var hrefArray = re.exec(h);

					var yID = hrefArray[idx];
					var yData = youtubetooltip.youtubeData(yID);
	
					var tipLabel = yData.title;
					if(youtubetooltip.prefs['showauthor'])
					{
						tipLabel += "\nAuthor: " + yData.author;
					}
		
					if(youtubetooltip.prefs['showduration'])
					{
						tipLabel += "\nDuration: " + yData.duration;
					}
	
					if(youtubetooltip.prefs['showrating'])
					{
						tipLabel += "\nRating: " + yData.rating;
					}
	
					if(youtubetooltip.prefs['showviews'])
					{
						tipLabel += "\nViews: " + yData.views;
					}
	
					if(youtubetooltip.prefs['showdate'])
					{
						tipLabel += "\nDate: " + yData.date;
					}
	
					if(youtubetooltip.prefs['showdescription'])
					{
						tipLabel += "\nDescription: " + yData.description;
					}
	
					if(youtubetooltip.prefs['showcategory'])
					{
						tipLabel += "\nCategory: " + yData.category;
					}
	
					if(youtubetooltip.prefs['showtags'])
					{
						tipLabel += "\nTags: " + yData.tags;
					}
	
					if(youtubetooltip.prefs['showcomments'])
					{
						tipLabel += "\nComments: " + yData.comments;
					}
	
					if(youtubetooltip.prefs['showresponses'])
					{
						tipLabel += "\nResponses: " + yData.responses;
					}
	
					if(tipLabel != null && yData.title != null)
					{
						tipNode.setAttribute('label', tipLabel);
						//tipNode.setAttribute('noautohide', true);
						retVal = true;
					}
				}
			}
		}

		if(!retVal)
		{
			return youtubetooltip.originalTooltip(tipElement);
		}

		return retVal;
	},
};
window.addEventListener('load', function(e) { youtubetooltip.onLoad(e); }, false);
window.addEventListener('unload', function(e) { youtubetooltip.onUnload(e); }, false);
